package com.google.code.synchunit;

import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class SynchUnit {

	private static SynchUnit instance;

	private List<MonitoredThread> monitoredThreads;

	private SynchUnit() {
		monitoredThreads = new ArrayList<MonitoredThread>();
	}

	public static SynchUnit getInstance() {
		if (instance == null)
			instance = new SynchUnit();
		return instance;
	}

	public void monitor(MonitoredThread t) {
		if (monitoredThreads.contains(t)) {
			throw new IllegalArgumentException("Thread " + t
					+ " was already registered with SynchUnit.");
		}
		monitoredThreads.add(t);
	}

	public void suspendHere() {
		Thread threadSuspended = Thread.currentThread();
		MonitoredThread monitoredThread = checkThreadIsMonitored(threadSuspended);
		waitSuspensionForAllMonitoredThreadsExceptFor(monitoredThread);
		monitoredThread.suspendExecution();
	}

	private MonitoredThread checkThreadIsMonitored(Thread t) {
		for (MonitoredThread managedThread : monitoredThreads) {
			if (managedThread.equals(t)) {
				return managedThread;
			}
		}
		throw new IllegalArgumentException("Thread " + t
				+ " was not registered.");
	}

	private void waitSuspensionForAllMonitoredThreadsExceptFor(MonitoredThread threadToExclude) {
		while (true) {
			List<MonitoredThread> monitoredThreadsToCheck = new ArrayList<MonitoredThread>(
					monitoredThreads);
			monitoredThreadsToCheck.remove(threadToExclude);
			Iterator<MonitoredThread> it = monitoredThreadsToCheck.iterator();
			while (it.hasNext()) {
				MonitoredThread monitoredThread = it.next();
				State monitoredThreadState = monitoredThread.getState();
				if (!monitoredThreadState.equals(State.RUNNABLE)
						&& !monitoredThreadState.equals(State.TIMED_WAITING)) {
					it.remove();
				}
			}
			if (monitoredThreadsToCheck.isEmpty()) {
				break;
			}
			try {
				Thread.sleep(40);
			} catch (InterruptedException e) {
				throw new RuntimeException(
						"Waitin for monitored Threads to Suspend interrupted.",
						e);
			}
		}
	}

}
